/** 格式化类, 基于Marco Paland的printf版本进行修改, 增加时间日期打印格式
 *      "T": 时间日期, "-T": 日期, "+T": 时间
 * @file printf.hpp
 * @author Kiven Lee
 * @version 1.0
 * @date 2021-09-11
 *
 * @copyright Kivensoft (c) 2018 - 2021
 *
 */

///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
//             2014-2019, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
//        embedded systems with a very limited resources.
//        Use this instead of bloated standard/newlib printf.
//        These routines are thread safe and reentrant.
//
///////////////////////////////////////////////////////////////////////////////

#pragma once

#include <cstddef>
#include <cstdarg>
#include <cstring>
#include <string>

namespace klib {

    /**
     * Tiny vprintf implementation
     * @param format A string that specifies the format of the output
     * @param va A value identifying a variable arguments list
     * @return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
     */
    size_t vprintf(const char* format, va_list va);

    size_t vsnprintf(char* buffer, size_t count, const char* format, va_list va);

    inline size_t vsprintf(char* buffer, const char* format, va_list va) {
        return klib::vsnprintf(buffer, (size_t)-1, format, va);
    }

    /**
     * Tiny printf implementation
     * You have to implement _putchar if you use printf()
     * To avoid conflicts with the regular printf() API it is overridden by macro defines
     * and internal underscore-appended functions like printf_() are used
     * @param format A string that specifies the format of the output
     * @return The number of characters that are written into the array, not counting the terminating null character
     */
    inline size_t printf(const char* format, ...) {
        va_list va;
        va_start(va, format);
        size_t ret = klib::vprintf(format, va);
        va_end(va);
        return ret;
    }

    /**
     * Tiny sprintf implementation
     * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!
     * @param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!
     * @param format A string that specifies the format of the output
     * @return The number of characters that are WRITTEN into the buffer, not counting the terminating null character
     */
    inline size_t sprintf(char* buffer, const char* format, ...) {
        va_list va;
        va_start(va, format);
        size_t ret = klib::vsnprintf(buffer, (size_t)-1, format, va);
        va_end(va);
        return ret;
    }

    /**
     * Tiny snprintf/vsnprintf implementation
     * @param buffer A pointer to the buffer where to store the formatted string
     * @param count The maximum number of characters to store in the buffer, including a terminating null character
     * @param format A string that specifies the format of the output
     * @param va A value identifying a variable arguments list
     * @return The number of characters that COULD have been written into the buffer, not counting the terminating
     *         null character. A value equal or larger than count indicates truncation. Only when the returned value
     *         is non-negative and less than count, the string has been completely written.
     */
    inline size_t snprintf(char* buffer, size_t count, const char* format, ...) {
        va_list va;
        va_start(va, format);
        size_t ret = klib::vsnprintf(buffer, count, format, va);
        va_end(va);
        return ret;
    }

    class printf_private {
    private:
        // friend size_t bufvprintf(void (*) (const char*, size_t, void*), void*, const char*, va_list);
        template<typename T>
        friend size_t bufvprintf(void (*out) (const char* data, size_t len, T* arg), T* arg, const char* format, va_list va);

        template<typename T>
        friend size_t vfctprintf(void (*out)(char character, T* arg), T* arg, const char* format, va_list va);

        // 内部实现结构，仅供bufprintf使用，用户无需关心实现细节
        struct _printf_buffer {
            void (*out) (const char*, size_t, void*);
            void *   arg;
            uint32_t pos;
            uint32_t size;
            char *   buf;
        };

        // 内部实现函数，仅供bufprintf使用，用户不应自行调用
        static size_t _bufvprintf(_printf_buffer* arg, const char* format, va_list va);
        static size_t _vfctprintf(void (*out)(char character, void* arg), void* arg, const char* format, va_list va);
    };

    /**
     * printf with output function
     * You may use this as dynamic alternative to printf() with its fixed _putchar() output
     * @param out An output function which takes one character and an argument pointer
     * @param arg An argument pointer for user data passed to output function
     * @param format A string that specifies the format of the output
     * @param va A value identifying a variable arguments list
     * @return The number of characters that are sent to the output function, not counting the terminating null character
     */
    template<typename T>
    inline size_t vfctprintf(void (*out)(char character, T* arg), T* arg, const char* format, va_list va) {
        return printf_private::_vfctprintf((void(*)(char, void*)) out, (void*) arg, format, va);
    }

    /**
     * printf with output function
     * You may use this as dynamic alternative to printf() with its fixed _putchar() output
     * @param out An output function which takes one character and an argument pointer
     * @param arg An argument pointer for user data passed to output function
     * @param format A string that specifies the format of the output
     * @return The number of characters that are sent to the output function, not counting the terminating null character
     */
    template<typename T>
    inline size_t fctprintf(void (*out)(char character, T* arg), T* arg, const char* format, ...) {
        va_list va;
        va_start(va, format);
        size_t ret = klib::vfctprintf<T>(out, arg, format, va);
        va_end(va);
        return ret;
    }

    // template<typename T>
    // static inline size_t bufvprintf(void (*out) (const char* data, size_t len, T* arg), T* arg, const char* format, va_list va);

    /** 基于自带缓冲区的格式化，用户自定义缓冲区大小
     * bufprintf with output function
     * You may use this as dynamic alternative to printf() with its fixed _putchar() output
     * @param out An output function which takes one character and an argument pointer
     * @param arg An argument pointer for user data passed to output function
     * @param format A string that specifies the format of the output
     * @param va A value identifying a variable arguments list
     * @return The number of characters that are sent to the output function, not counting the terminating null character
     */
    template<typename T, uint32_t _Num = 256>
    inline size_t bufvprintf(void (*out) (const char* data, size_t len, T* arg), T* arg, const char* format, va_list va) {
        char local_buf[_Num];
        printf_private::_printf_buffer buf = {
                .out = (void(*)(const char*, size_t, void*)) out,
                .arg = (void*) arg, .pos = 0, .size = _Num, .buf = local_buf };
        return printf_private::_bufvprintf(&buf, format, va);
    }

    /** 基于自带缓冲区的格式化，用户自定义缓冲区大小
     * bufprintf with output function
     * You may use this as dynamic alternative to printf() with its fixed _putchar() output
     * @param out An output function which takes one character and an argument pointer
     * @param arg An argument pointer for user data passed to output function
     * @param format A string that specifies the format of the output
     * @param va A value identifying a variable arguments list
     * @return The number of characters that are sent to the output function, not counting the terminating null character
     */
    template<typename T, uint32_t _Num = 256>
    inline size_t bufprintf(void (*out) (const char* data, size_t len, T* arg), T* arg, const char* format, ...) {
        va_list va;
        va_start(va, format);
        int r = klib::bufvprintf<T, _Num>(out, arg, format, va);
        va_end(va);
        return r;
    }

    inline std::string& vsprintf(std::string& out, const char* format, va_list va) {
        va_list tmp;
        va_copy(tmp, va);
        size_t pos = out.length();
        size_t len = klib::vsnprintf(nullptr, (size_t)-1, format, tmp);
        out.resize(pos + len);
        klib::vsnprintf(out.data() + pos, len + 1, format, va);
        return out;
    }

    inline std::string& sprintf(std::string& out, const char* format, ...) {
        va_list va;
        va_start(va, format);
        klib::vsprintf(out, format, va);
        va_end(va);
        return out;
    }

} // namespace klib
